<?php
declare(strict_types=1);

namespace MongoDB\Examples\CommandLogger;

use Closure;
use Exception;
use MongoDB\BSON\Document;
use MongoDB\Client;
use MongoDB\Driver\Monitoring\CommandFailedEvent;
use MongoDB\Driver\Monitoring\CommandStartedEvent;
use MongoDB\Driver\Monitoring\CommandSubscriber;
use MongoDB\Driver\Monitoring\CommandSucceededEvent;

use function assert;
use function getenv;
use function is_object;
use function printf;

require __DIR__ . '/../vendor/autoload.php';

function toJSON(object $document): string
{
    return Document::fromPHP($document)->toRelaxedExtendedJSON();
}

class CommandLogger implements CommandSubscriber
{
    /** @param Closure(object):void $handleOutput */
    public function __construct(private readonly Closure $handleOutput)
    {
    }

    public function commandStarted(CommandStartedEvent $event): void
    {
        $this->handleOutput->__invoke($event);
    }

    public function commandSucceeded(CommandSucceededEvent $event): void
    {
        $this->handleOutput->__invoke($event);
    }

    public function commandFailed(CommandFailedEvent $event): void
    {
        $this->handleOutput->__invoke($event);
    }
}

$client = new Client(getenv('MONGODB_URI') ?: 'mongodb://127.0.0.1/');

$handleOutput = function (object $event): void {
    switch ($event::class) {
        case CommandStartedEvent::class:
            printf("%s command started\n", $event->getCommandName());
            printf("command: %s\n", toJson($event->getCommand()));
            break;
        case CommandSucceededEvent::class:
            printf("%s command succeeded\n", $event->getCommandName());
            printf("reply: %s\n", toJson($event->getReply()));
            break;
        case CommandFailedEvent::class:
            printf("%s command failed\n", $event->getCommandName());
            printf("reply: %s\n", toJson($event->getReply()));

            $exception = $event->getError();
            printf("exception: %s\n", $exception::class);
            printf("exception.code: %d\n", $exception->getCode());
            printf("exception.message: %s\n", $exception->getMessage());
            break;
        default:
            throw new Exception('Event type not supported');
    }

    echo "\n";
};

$client->addSubscriber(new CommandLogger($handleOutput));

$collection = $client->test->command_logger;
$collection->drop();

$collection->insertMany([
    ['x' => 1],
    ['x' => 2],
    ['x' => 3],
]);

$collection->updateMany(
    ['x' => ['$gt' => 1]],
    ['$set' => ['y' => 1]],
);

$cursor = $collection->find([], ['batchSize' => 2]);

foreach ($cursor as $document) {
    assert(is_object($document));
    printf("%s\n", toJSON($document));
}
